from pynumpak.type import func_t

from .differentiation import diff


def newton(func: func_t, initial_value: float, max_iter: int = 100, x_tol: float = 1e-12,
           f_tol: float = 1e-16, show_iter_time: bool = False):
    def g(x_):
        return x_ - func(x_) / diff(func, x_)

    x = initial_value
    for i in range(max_iter):
        x = g(x)
        if abs(func(x)) < f_tol:
            return (x, i + 1) if show_iter_time else x
    else:
        if abs(func(x) / diff(func, x)) < x_tol:
            return (x, i + 1) if show_iter_time else x
        else:
            raise (ValueError("Unable to coverage"))


def secant(func: func_t, initial_value: float, max_iter: int = 100, x_tol: float = 1e-12,
           f_tol: float = 1e-16, show_iter_time: bool = False):
    def g1(x_):
        return x_ - func(x_) / diff(func, x_)

    def g(x1_, x2_):
        if (func(x1_) - func(x2_)) == 0:
            return x1_, x1_
        return x1_ - func(x1_) * (x1_ - x2_) / (func(x1_) - func(x2_)), x1_

    x1, x2 = g1(initial_value), initial_value
    for i in range(max_iter):
        x1, x2 = g(x1, x2)
        if abs(func(x1)) < f_tol:
            return (x1, i + 2) if show_iter_time else x1
    else:
        if abs(g(x1, x2)[0] - x1) < x_tol:
            return (x1, i + 2) if show_iter_time else x1
        else:
            raise (ValueError("Unable to coverage"))


def steffensen(func: func_t, initial_value: float, max_iter: int = 100, x_tol: float = 1e-10,
               f_tol: float = 1e-14, show_iter_time: bool = False):
    def g(x_):
        return x_ - func(x_) ** 2 / (func(func(x_) + x_) - func(x_))

    x = initial_value
    for i in range(max_iter):
        x = g(x)
        if abs(func(x)) < f_tol:
            return (x, i + 1) if show_iter_time else x
    else:
        if abs(func(x) ** 2 / (func(func(x) + x) - func(x))) < x_tol:
            return (x, i + 1) if show_iter_time else x
        else:
            raise (ValueError("Unable to coverage"))
